home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 1.iso / DEMON / RISCOS2 / TCP_131S.ARC / c / resolve_0 < prev    next >
Text File  |  1993-12-22  |  16KB  |  680 lines

  1. /*
  2.  * Copyright (c) 1985 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted provided
  6.  * that: (1) source distributions retain this entire copyright notice and
  7.  * comment, and (2) distributions including binaries display the following
  8.  * acknowledgement:  ``This product includes software developed by the
  9.  * University of California, Berkeley and its contributors'' in the
  10.  * documentation or other materials provided with the distribution and in
  11.  * all advertising materials mentioning features or use of this software.
  12.  * Neither the name of the University nor the names of its contributors may
  13.  * be used to endorse or promote products derived from this software without
  14.  * specific prior written permission.
  15.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  16.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  17.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  18.  */
  19.  
  20. #if defined(LIBC_SCCS) && !defined(lint)
  21. static char sccsid[] = "@(#)res_comp.c    6.18 (Berkeley) 6/27/90";
  22. #endif /* LIBC_SCCS and not lint */
  23.  
  24. #include "netdb.h"
  25. #include "resolve.h"
  26. #include <stdlib.h>
  27. #include <stdio.h>
  28. #include "arpa/nameser.h"
  29. #include "netinet/in.h"
  30. #include "socket.h"
  31. #include "time.h"
  32.  
  33. static dn_find();
  34.  
  35. /*
  36.  * Expand compressed domain name 'comp_dn' to full domain name.
  37.  * 'msg' is a pointer to the begining of the message,
  38.  * 'eomorig' points to the first location after the message,
  39.  * 'exp_dn' is a pointer to a buffer of size 'length' for the result.
  40.  * Return size of compressed name or -1 if there was an error.
  41.  */
  42. dn_expand(msg, eomorig, comp_dn, exp_dn, length)
  43.     u_char *msg, *eomorig, *comp_dn, *exp_dn;
  44.     int length;
  45. {
  46.     register u_char *cp, *dn;
  47.     register int n, c;
  48.     u_char *eom;
  49.     int len = -1, checked = 0;
  50.  
  51.     dn = exp_dn;
  52.     cp = comp_dn;
  53.     eom = exp_dn + length;
  54.     /*
  55.      * fetch next label in domain name
  56.      */
  57.     while (n = *cp++) {
  58.         /*
  59.          * Check for indirection
  60.          */
  61.         switch (n & INDIR_MASK) {
  62.         case 0:
  63.             if (dn != exp_dn) {
  64.                 if (dn >= eom)
  65.                     return (-1);
  66.                 *dn++ = '.';
  67.             }
  68.             if (dn+n >= eom)
  69.                 return (-1);
  70.             checked += n + 1;
  71.             while (--n >= 0) {
  72.                 if ((c = *cp++) == '.') {
  73.                     if (dn + n + 2 >= eom)
  74.                         return (-1);
  75.                     *dn++ = '\\';
  76.                 }
  77.                 *dn++ = c;
  78.                 if (cp >= eomorig)    /* out of range */
  79.                     return(-1);
  80.             }
  81.             break;
  82.  
  83.         case INDIR_MASK:
  84.             if (len < 0)
  85.                 len = cp - comp_dn + 1;
  86.             cp = msg + (((n & 0x3f) << 8) | (*cp & 0xff));
  87.             if (cp < msg || cp >= eomorig)    /* out of range */
  88.                 return(-1);
  89.             checked += 2;
  90.             /*
  91.              * Check for loops in the compressed name;
  92.              * if we've looked at the whole message,
  93.              * there must be a loop.
  94.              */
  95.             if (checked >= eomorig - msg)
  96.                 return (-1);
  97.             break;
  98.  
  99.         default:
  100.             return (-1);            /* flag error */
  101.         }
  102.     }
  103.     *dn = '\0';
  104.     if (len < 0)
  105.         len = cp - comp_dn;
  106.     return (len);
  107. }
  108.  
  109. /*
  110.  * Compress domain name 'exp_dn' into 'comp_dn'.
  111.  * Return the size of the compressed name or -1.
  112.  * 'length' is the size of the array pointed to by 'comp_dn'.
  113.  * 'dnptrs' is a list of pointers to previous compressed names. dnptrs[0]
  114.  * is a pointer to the beginning of the message. The list ends with NULL.
  115.  * 'lastdnptr' is a pointer to the end of the arrary pointed to
  116.  * by 'dnptrs'. Side effect is to update the list of pointers for
  117.  * labels inserted into the message as we compress the name.
  118.  * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
  119.  * is NULL, we don't update the list.
  120.  */
  121. dn_comp(exp_dn, comp_dn, length, dnptrs, lastdnptr)
  122.     u_char *exp_dn, *comp_dn;
  123.     int length;
  124.     u_char **dnptrs, **lastdnptr;
  125. {
  126.     register u_char *cp, *dn;
  127.     register int c, l;
  128.     u_char **cpp, **lpp, *sp, *eob;
  129.     u_char *msg;
  130.  
  131.     dn = exp_dn;
  132.     cp = comp_dn;
  133.     eob = cp + length;
  134.     if (dnptrs != NULL) {
  135.         if ((msg = *dnptrs++) != NULL) {
  136.             for (cpp = dnptrs; *cpp != NULL; cpp++)
  137.                 ;
  138.             lpp = cpp;    /* end of list to search */
  139.         }
  140.     } else
  141.         msg = NULL;
  142.     for (c = *dn++; c != '\0'; ) {
  143.         /* look to see if we can use pointers */
  144.         if (msg != NULL) {
  145.             if ((l = dn_find(dn-1, msg, dnptrs, lpp)) >= 0) {
  146.                 if (cp+1 >= eob)
  147.                     return (-1);
  148.                 *cp++ = (l >> 8) | INDIR_MASK;
  149.                 *cp++ = l % 256;
  150.                 return (cp - comp_dn);
  151.             }
  152.             /* not found, save it */
  153.             if (lastdnptr != NULL && cpp < lastdnptr-1) {
  154.                 *cpp++ = cp;
  155.                 *cpp = NULL;
  156.             }
  157.         }
  158.         sp = cp++;    /* save ptr to length byte */
  159.         do {
  160.             if (c == '.') {
  161.                 c = *dn++;
  162.                 break;
  163.             }
  164.             if (c == '\\') {
  165.                 if ((c = *dn++) == '\0')
  166.                     break;
  167.             }
  168.             if (cp >= eob) {
  169.                 if (msg != NULL)
  170.                     *lpp = NULL;
  171.                 return (-1);
  172.             }
  173.             *cp++ = c;
  174.         } while ((c = *dn++) != '\0');
  175.         /* catch trailing '.'s but not '..' */
  176.         if ((l = cp - sp - 1) == 0 && c == '\0') {
  177.             cp--;
  178.             break;
  179.         }
  180.         if (l <= 0 || l > MAXLABEL) {
  181.             if (msg != NULL)
  182.                 *lpp = NULL;
  183.             return (-1);
  184.         }
  185.         *sp = l;
  186.     }
  187.     if (cp >= eob) {
  188.         if (msg != NULL)
  189.             *lpp = NULL;
  190.         return (-1);
  191.     }
  192.     *cp++ = '\0';
  193.     return (cp - comp_dn);
  194. }
  195.  
  196. /*
  197.  * Skip over a compressed domain name. Return the size or -1.
  198.  */
  199. dn_skipname(comp_dn, eom)
  200.     u_char *comp_dn, *eom;
  201. {
  202.     register u_char *cp;
  203.     register int n;
  204.  
  205.     cp = comp_dn;
  206.     while (cp < eom && (n = *cp++)) {
  207.         /*
  208.          * check for indirection
  209.          */
  210.         switch (n & INDIR_MASK) {
  211.         case 0:        /* normal case, n == len */
  212.             cp += n;
  213.             continue;
  214.         default:    /* illegal type */
  215.             return (-1);
  216.         case INDIR_MASK:    /* indirection */
  217.             cp++;
  218.         }
  219.         break;
  220.     }
  221.     return (cp - comp_dn);
  222. }
  223.  
  224. /*
  225.  * Search for expanded name from a list of previously compressed names.
  226.  * Return the offset from msg if found or -1.
  227.  * dnptrs is the pointer to the first name on the list,
  228.  * not the pointer to the start of the message.
  229.  */
  230. static
  231. dn_find(exp_dn, msg, dnptrs, lastdnptr)
  232.     u_char *exp_dn, *msg;
  233.     u_char **dnptrs, **lastdnptr;
  234. {
  235.     register u_char *dn, *cp, **cpp;
  236.     register int n;
  237.     u_char *sp;
  238.  
  239.     for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
  240.         dn = exp_dn;
  241.         sp = cp = *cpp;
  242.         while (n = *cp++) {
  243.             /*
  244.              * check for indirection
  245.              */
  246.             switch (n & INDIR_MASK) {
  247.             case 0:        /* normal case, n == len */
  248.                 while (--n >= 0) {
  249.                     if (*dn == '.')
  250.                         goto next;
  251.                     if (*dn == '\\')
  252.                         dn++;
  253.                     if (*dn++ != *cp++)
  254.                         goto next;
  255.                 }
  256.                 if ((n = *dn++) == '\0' && *cp == '\0')
  257.                     return (sp - msg);
  258.                 if (n == '.')
  259.                     continue;
  260.                 goto next;
  261.  
  262.             default:    /* illegal type */
  263.                 return (-1);
  264.  
  265.             case INDIR_MASK:    /* indirection */
  266.                 cp = msg + (((n & 0x3f) << 8) | *cp);
  267.             }
  268.         }
  269.         if (*dn == '\0')
  270.             return (sp - msg);
  271.     next:    ;
  272.     }
  273.     return (-1);
  274. }
  275.  
  276. /*
  277.  * Routines to insert/extract short/long's. Must account for byte
  278.  * order and non-alignment problems. This code at least has the
  279.  * advantage of being portable.
  280.  *
  281.  * used by sendmail.
  282.  */
  283.  
  284. u_short
  285. _getshort(msgp)
  286.     u_char *msgp;
  287. {
  288.     register u_char *p = (u_char *) msgp;
  289. #ifdef vax
  290.     /*
  291.      * vax compiler doesn't put shorts in registers
  292.      */
  293.     register u_long u;
  294. #else
  295.     register u_short u;
  296. #endif
  297.  
  298.     u = *p++ << 8;
  299.     return ((u_short)(u | *p));
  300. }
  301.  
  302. u_long
  303. _getlong(msgp)
  304.     u_char *msgp;
  305. {
  306.     register u_char *p = (u_char *) msgp;
  307.     register u_long u;
  308.  
  309.     u = *p++; u <<= 8;
  310.     u |= *p++; u <<= 8;
  311.     u |= *p++; u <<= 8;
  312.     return (u | *p);
  313. }
  314.  
  315.  
  316. putshort(s, msgp)
  317.     register u_short s;
  318.     register u_char *msgp;
  319. {
  320.  
  321.     msgp[1] = s;
  322.     msgp[0] = s >> 8;
  323. }
  324.  
  325. putlong(l, msgp)
  326.     register u_long l;
  327.     register u_char *msgp;
  328. {
  329.  
  330.     msgp[3] = l;
  331.     msgp[2] = (l >>= 8);
  332.     msgp[1] = (l >>= 8);
  333.     msgp[0] = l >> 8;
  334. }
  335.  
  336.  
  337.  
  338.  
  339.  
  340. static struct cache_s {
  341.   void *key;
  342.   int type;
  343.   int len;
  344.   struct hostent *cont;
  345.   time_t expires;
  346.   time_t accessed;
  347.   } cache[CACHE_SIZE];
  348.  
  349.  
  350. static void kill_cache( struct cache_s *entry )
  351. {
  352.   if( ! entry->key ) return;
  353.   free( entry->key );
  354.   entry->cont = NULL;
  355.   entry->key = NULL;
  356. }
  357.  
  358.  
  359. static int he_size( struct hostent *he )
  360. {
  361.   int size;
  362.   char **cp;
  363.   struct mx_data **mx;
  364.  
  365.   if( !he ) return 0;
  366.   size = sizeof( struct hostent ) + 2*sizeof(char *) + ((strlen(he->h_name)+4)&~3) + sizeof(struct mx_data *);
  367.   for( cp=he->h_aliases; *cp; cp++ ) size += sizeof(char *) + strlen(*cp)+1;
  368.   for( cp=he->h_addr_list; *cp; cp++ ) size += sizeof(char *) + he->h_length;
  369.   for( mx=he->h_mx; *mx; mx++ ) size += sizeof(struct mx_data) + strlen((*mx)->host) + sizeof(struct mx_data *) + 1;
  370.   return size;
  371. }
  372.  
  373.  
  374. static struct hostent *he_copy( char *mem, struct hostent *he )
  375. {
  376.   struct hostent *new;
  377.   int i;
  378.   char **cp;
  379.   char **ds;
  380.   struct mx_data **mx,**xd;
  381.  
  382.   if( !he ) return NULL;
  383.   new = (struct hostent *)mem;
  384.   memcpy( new, he, sizeof(*new) );
  385.   mem += sizeof(*new);
  386.   for( cp=he->h_aliases,i=1; *cp; cp++,i++ );
  387.   new->h_aliases = (char **)mem;
  388.   mem += i*sizeof(char *);
  389.   for( cp=he->h_addr_list,i=1; *cp; cp++,i++ );
  390.   new->h_addr_list = (char **)mem;
  391.   mem += i*sizeof(char *);
  392.   for( mx=he->h_mx,i=1; *mx; mx++,i++ );
  393.   new->h_mx = (struct mx_data **)mem;
  394.   mem += i*sizeof(struct mx_data *);
  395.   for( mx=new->h_mx; i>1; i--,mx++,mem+=sizeof(struct mx_data) ) *mx = (struct mx_data *)mem;
  396.   *mx = NULL;
  397.  
  398.   strcpy( mem, he->h_name );
  399.   new->h_name = mem;
  400.   mem += ((strlen( he->h_name ) + 4)&~3);
  401.  
  402.   for( ds=new->h_addr_list,cp=he->h_addr_list; *cp; cp++,ds++ ) {
  403.     *ds = mem;
  404.     memcpy( mem, *cp, he->h_length );
  405.     mem += he->h_length;
  406.     }
  407.   *ds = NULL;
  408.  
  409.   for( ds=new->h_aliases,cp=he->h_aliases; *cp; cp++,ds++ ) {
  410.     *ds = mem;
  411.     i = strlen( *cp ) +1;
  412.     memcpy( mem, *cp, i );
  413.     mem += i;
  414.     }
  415.   *ds = NULL;
  416.  
  417.   for( xd=new->h_mx,mx=he->h_mx; *mx; mx++,xd++ ) {
  418.     (*xd)->preference = (*mx)->preference;
  419.     (*xd)->host = (char *)mem;
  420.     i = strlen( (*mx)->host) + 1;
  421.     memcpy( mem, (*mx)->host, i );
  422.     mem += i;
  423.     }
  424.   *xd = NULL;
  425.  
  426.   return new;
  427. }
  428.  
  429.  
  430. extern int cache_req( void *, int, int , struct hostent ** );
  431. int cache_req( void *data, int type, int len, struct hostent **rval )
  432. {
  433.   struct cache_s *find;
  434.   int i;
  435.   time_t t;
  436.  
  437.   for( find=cache,i=CACHE_SIZE; i; i--,find++ )
  438.    if( (find->key) && (find->type==type) && (find->len==len) && ! memcmp( find->key, data, len ) ) {
  439.     time( &t );
  440.     if( difftime( t, find->expires ) >= 0 ) kill_cache( find );
  441.      else {
  442.        *rval = find->cont;
  443.        find->accessed = time(NULL);
  444.        return 0;
  445.        }
  446.     }
  447.   return -1;
  448. }
  449.  
  450.  
  451. void cache_it( void *data, int type, int len, struct hostent *result )
  452. {
  453.   struct cache_s *overw=NULL, *clear=NULL;
  454.   int i;
  455.   time_t min;
  456.   char *mem;
  457.  
  458.   time(&min);
  459.   for( overw=cache,i=CACHE_SIZE; i; i--,overw++ ) if( overw->key && difftime(min,overw->expires)<0 ) {
  460.     if( overw->type==type && overw->len==len && ! memcmp( overw->key, data, len ) ) break;
  461.     }
  462.    else clear = overw;
  463.   if( !i ) overw = clear;
  464.   if( ! overw ) for( overw=cache,clear=&cache[1],i=CACHE_SIZE-1,min=cache->accessed; i; i--,clear++ )
  465.    if( difftime( clear->accessed, min ) < 0 ) {
  466.     overw = clear;
  467.     min = clear->accessed;
  468.     }
  469.  
  470.   mem = malloc( len+he_size(result)+3 );
  471.   if( ! mem ) return;
  472.   kill_cache( overw );
  473.   memcpy( mem, data, len );
  474.   overw->key = mem;
  475.   overw->type = type;
  476.   overw->len = len;
  477.   overw->accessed = time( NULL );
  478.   overw->expires = overw->accessed + CACHE_OBSOLETE;
  479.   len = (len+3)&~3;
  480.   overw->cont = he_copy( mem+len, result );
  481. }
  482.  
  483.  
  484. void cache_save( void )
  485. {
  486.   FILE *f;
  487.   int i;
  488.   int n;
  489.   struct cache_s *cs;
  490.   char **cp;
  491.   struct mx_data **mx;
  492.  
  493.   f = fopen( "<TCPIP$dir>.resolve.cache", "w" );
  494.   if( ! f ) return;
  495.   for( n=CACHE_SIZE,cs=cache; n; n--,cs++ ) if( cs->key ) {
  496.     switch( cs->type ) {
  497.       case CACHE_MX:
  498.         fprintf( f, "MX " );
  499.         goto keysave;
  500.       case CACHE_NAME:
  501.         fprintf( f, "NAME " );
  502.     keysave:
  503.         fwrite( cs->key, cs->len, 1, f );
  504.         fprintf( f, "\n" );
  505.         break;
  506.       }
  507.     fprintf( f, "%d %d\n", cs->expires, cs->accessed );
  508.     if( cs->cont ) {
  509.       for( mx=cs->cont->h_mx; *mx; mx++ ) {
  510.         fprintf( f, "%s", (*mx)->host );
  511.         if( mx[1] ) fputc( ' ', f );
  512.         }
  513.       fputc( '\n', f );
  514.       fprintf( f, "%d\n%s\n%d\n", cs->cont->h_length, cs->cont->h_name, cs->cont->h_addrtype );
  515.       for( cp=cs->cont->h_aliases; *cp; cp++ ) fprintf( f, "%s ", *cp );
  516.       fputc( '\n', f );
  517.       for( cp=cs->cont->h_addr_list; *cp; cp++ ) {
  518.         for( i=cs->cont->h_length; i; i-- ) fprintf( f, "%d.", (*(unsigned char **)cp)[i-1] );
  519.         fputc( ' ', f );
  520.         }
  521.       fputc( '\n', f );
  522.       }
  523.      else fprintf( f, "\n%d\n",
  524.      0 );
  525.     if( n>1 ) fputc( '\n', f );
  526.     }
  527.   fclose( f );    
  528. }
  529.  
  530.  
  531. void cache_restore( void )
  532. {
  533.   char buffer[500];
  534.   char hostname[256];
  535.   char addrs[100];
  536.   char aliases[500];
  537.   char mxhosts[500];
  538.   struct mx_data mxdata[30];
  539.   struct mx_data *mxptr[30];
  540.   struct mx_data **mxpp;
  541.   char key[256];
  542.   struct hostent he;
  543.   char *p_aliases[30];
  544.   char *p_addrs[35];
  545.   char *next;
  546.   char **at;
  547.   char *nextabyte;
  548.   FILE *f;
  549.   struct cache_s *c;
  550.   int i,j;
  551.   int n;
  552.   struct hostent *ohe;
  553.  
  554.   f = fopen( "<TCPIP$dir>.resolve.cache", "r" );
  555.   if( ! f ) return;
  556.   cache_purge();
  557.   he.h_aliases = p_aliases;
  558.   he.h_addr_list = p_addrs;
  559.   he.h_mx = mxptr;
  560.   for( i=CACHE_SIZE,c=cache; i &&  ! feof(f) && ! ferror(f); i--,c++ ) {
  561.  
  562.     c->cont = NULL;
  563.     fgets( buffer, 255, f );
  564.     if( ! memcmp( buffer, "NAME ", 5 ) ) {
  565.       c->type = CACHE_NAME;
  566.       memcpy( key, &buffer[5], strlen(buffer)-4 );
  567.       c->len = strlen( key );
  568.       if( key[c->len-1] == '\n' ) c->len--;
  569.       }
  570.      else if( ! memcmp( buffer, "MX ", 3 ) ) {
  571.       c->type = CACHE_MX;
  572.       memcpy( key, &buffer[3], strlen(buffer)-2 );
  573.       c->len = strlen( key );
  574.       if( key[c->len-1] == '\n' ) c->len--;
  575.       }
  576.      else break;
  577.  
  578.     fgets( buffer, 255, f );
  579.     if( sscanf( buffer, "%d %d", &c->expires, &c->accessed ) < 2 ) break;
  580.  
  581.     fgets( mxhosts, 499, f );
  582.     mxpp = mxptr;
  583.     for( j=0,next=mxhosts; *next != '\n' && *next; j++, mxpp++ ) {
  584.       while( *next == ' ' || *next == '\t' ) next++;
  585.       if( ! *next || *next == '\n' ) break;
  586.       *mxpp = &mxdata[j];
  587.       (*mxpp)->preference = j;
  588.       (*mxpp)->host = next;
  589.       while( *next != ' ' && *next && *next != '\t' && *next != '\n' ) next++;
  590.       if( *next == '\n' ) *next = 0;
  591.        else *(next++) = 0;
  592.       }
  593.     *mxpp = NULL;
  594.  
  595.     fgets( buffer, 255, f );
  596.     if( sscanf( buffer, "%d", &he.h_length ) < 1 ) break;
  597.     if( he.h_length ) ohe = &he; else ohe = NULL;
  598.  
  599.     if( ohe ) {
  600.       fgets( hostname, 255, f );
  601.       if( hostname[strlen(hostname)-1] == '\n' ) hostname[strlen(hostname)-1] = 0;
  602.       he.h_name = hostname;
  603.   
  604.       fgets( buffer, 255, f );
  605.       if( sscanf( buffer, "%d", &he.h_addrtype ) < 1 ) break;
  606.           
  607.       fgets( aliases, 499, f );
  608.       next = aliases;
  609.       for( n=29,at=he.h_aliases; n; at++,n-- ) {
  610.         while( *next==' ' || *next=='\t' ) next++;
  611.         if( *next=='\n' || *next==0 ) break;
  612.         *at = next;
  613.         while( *next != ' ' && *next && *next != '\t' && *next != '\n' ) next++;
  614.         *(next++) = 0;
  615.         }
  616.       *at = NULL;
  617.   
  618.       fgets( buffer, 499, f );
  619.       for( nextabyte=addrs,next=buffer,n=100,at=he.h_addr_list; n; n--,at++,nextabyte+=he.h_length ) {
  620.         while( *next==' ' || *next=='\t' ) next++;
  621.         if( *next=='\n' || *next==0 ) break;
  622.         *at = nextabyte;
  623.         for( j=he.h_length; j; j-- ) {
  624.           nextabyte[j-1] = atoi(next);
  625.           while( *next!='.' && *next!=' ' && *next && *next!='\t' ) next++;
  626.           if( *next ) next++;
  627.           }
  628.         if( *next=='.' ) next++;
  629.         }
  630.       *at = NULL;
  631.       }
  632.  
  633.     next = malloc( c->len + he_size( ohe ) + 3 );
  634.     memcpy( next, key, c->len );
  635.     c->key = next;
  636.     c->cont = he_copy( next + ((c->len+3)&~3), ohe );
  637.     fgets( buffer, 255, f );
  638.     }
  639.   fclose(f);
  640. }
  641.  
  642.  
  643. void cache_print( void )
  644. {
  645.   struct cache_s *c;
  646.   int i;
  647.   char buf[255];
  648.   time_t t;
  649.  
  650.   time( &t );
  651.   for( c=cache,i=CACHE_SIZE; i; i--,c++ ) if( c->key ) {
  652.     cwprintf(NULL, "type=" );
  653.     switch( c->type )
  654.     {
  655.       case CACHE_NAME:
  656.       case CACHE_MX:
  657.         memcpy( buf, c->key, c->len );
  658.         buf[c->len] = 0;
  659.         cwprintf(NULL, "%s, name=%s", c->type==CACHE_NAME?"NAME":"MX", buf );
  660.         break;
  661.       default:
  662.         cwprintf(NULL, "unknown" );
  663.       }
  664.     if( difftime( t, c->expires ) >= 0 )
  665.       cwprintf(NULL, ",  expired since" );
  666.     else
  667.       cwprintf(NULL, ",  expires at" );
  668.     cwprintf(NULL, " %s", ctime( &c->expires ) );
  669.     }
  670. }
  671.  
  672.  
  673. void cache_purge( void )
  674. {
  675.   int i;
  676.  
  677.   for( i=0; i<CACHE_SIZE; i++ ) kill_cache( &cache[i] );
  678. }
  679.  
  680.